(function (angular) {
	'use strict';
	angular.module("fs.waf")
		.provider("httpInterceptor", httpInterceptorProvider)
		.provider("httpParamSerializer", httpParamSerializerProvider)
		.config(['$httpProvider', function ($httpProvider) {
			$httpProvider.interceptors.push('httpInterceptor');
		}])
		.run(transformHttpRequestAndResponse);

	httpInterceptorProvider.$inject = [];
	function httpInterceptorProvider() {
		this.$get = httpInterceptor;
		var requestList = {};
		function addRequestKey(key) {
			requestList[key] = true;
			$timeout(function () {
				removeRequestKey(key);
			}, 500);
		}
		function removeRequestKey(key) {
			delete requestList[key];
		}
		function isDuplicateRequest(key) {
			return requestList[key];
		}
		function getRequestKey(request) {
			var requestKey = [];
			requestKey.push('Method: ' + request.method);
			requestKey.push('TransactionToken: ' + request.headers[Constants.TRANSACTION_TOKEN]);
			requestKey.push('CsrfToken: ' + request.headers[Constants.CSRF_TOKEN]);
			requestKey.push('Url: ' + request.url);
			if (angular.isObject(request.data)) {
				try {
					requestKey.push('Data: ' + angular.toJson(request.data));
				} catch (e) {
					requestKey.push('Data: ' + request.data);
				}
			}
			function hash(str) {
				var h = 0, i = 0, n;
				var strlen = str.length;
				if (strlen === 0) {
					return h;
				}
				for (i = 0, n; i < strlen; ++i) {
					n = str.charCodeAt(i);
					h = ((h << 5) - h) + n;
					h = h & h;
				}
				return h >>> 0;
			}
			return hash(requestKey.join(','));
		}
		httpInterceptor.$inject = ['$rootScope', '$timeout', 'Constants', 'loggerFactory', 'storageService', '$q', '$injector'];
		function httpInterceptor($rootScope, $timeout, Constants, loggerFactory, storageService, $q, $injector) {
			var isExistSuffix = function (objStr) {
				var urlSuffixArray = ["uniqueCheck", "lookupCheck", "holidays"];
				for (var i = 0; i < urlSuffixArray.length; i++) {
					if (objStr.indexOf(urlSuffixArray[i]) != -1) {
						return true;
						break;
					}
				}
				return false;
			};
			var interceptor = {
				'request': function (config) {
					var defered, log, csrfToken, transToken, requestKey;
					defered = $q.defer();
					log = loggerFactory.getLogger('request');
					config.headers[Constants.DEF_XHRHEADERNAME] = Constants.DEF_XHRHEADERVALUE;
					if (config.url.indexOf("do/") >= 0) {
						if (!isExistSuffix(config.url)) $injector.get('modalService').openLoading();
						csrfToken = Constants.CSRF_TOKEN ? storageService.getItemFromSession(Constants.CSRF_TOKEN) : undefined;
						transToken = Constants.TRANSACTION_TOKEN ? storageService.getItemFromSession(Constants.TRANSACTION_TOKEN) : undefined;
						if (csrfToken) {
							config.headers[Constants.CSRF_TOKEN] = csrfToken;
						}
						if (transToken) {
							config.headers[Constants.TRANSACTION_TOKEN] = transToken;
						}
						if (config.data && config.data.hasOwnProperty('taskId')) {
							config.headers['taskId'] = config.data.taskId;
						}
						defered.resolve(config);
						/*if (request.url.indexOf("do/p/") >= 0) {
						} else {
							requestKey = getRequestKey(request);
							if (isDuplicateRequest(requestKey)) {
								deferred.reject({
									errorCode : 'DUPLUCATE_REQUEST',
									errorMsg : "Duplicate Request: " + angular.toJson(request)
								});
							} else {
								addRequestKey(requestKey);
								deferred.resolve(request);
							}
						}*/
					} else if (config.url.indexOf(".html") > 0) {
						config.headers['Accept'] = 'text/html';
						defered.resolve(config);
					} else {
						defered.resolve(config);
					}
					return defered.promise;
				},
				'response': function (response) {
					var deferred, log, csrfToken, transToken, errorCode;
					if (response.config.url.indexOf("do/") >= 0) {
						if (!isExistSuffix(response.config.url)) $injector.get('modalService').closeLoading();
					}
					deferred = $q.defer();
					log = loggerFactory.getLogger('response');
					csrfToken = Constants.CSRF_TOKEN ? response.headers(Constants.CSRF_TOKEN) : undefined;
					transToken = Constants.TRANSACTION_TOKEN ? response.headers(Constants.TRANSACTION_TOKEN) : undefined;
					if (csrfToken) {
						storageService.setItemToSession(Constants.CSRF_TOKEN, csrfToken);
					}
					if (transToken) {
						storageService.setItemToSession(Constants.TRANSACTION_TOKEN, transToken);
					}
					if (response.status == 200) {
						if (response.error) {
							errorCode = response.error;
							if (errorCode == Constants.ERROR_CODE_SESSION_TIMEOUT) {
								$rootScope.$emit(Constants.NOTIFY_REDIRECT, {
									url: 'do/p/signon'
								});
								deferred.reject(response);
							} else if (errorCode == Constants.ERROR_CODE_UNAUTHORIZED) {
								$rootScope.$emit(Constants.NOTIFY_UNAUTHORIZED, response.error);
								deferred.reject(response);
							} else if (errorCode == Constants.ERROR_CODE_CUSTOMER_ERROR) {
								$rootScope.$emit(Constants.NOTIFY_CUSTOMER_ERROR, response.error);
								deferred.reject(response);
							} else {
								$rootScope.$emit(Constants.NOTIFY_SYSTEM_ERROR, response.error);
								deferred.reject(response);
							}
						} else {
							deferred.resolve(response);
						}
					}
					return deferred.promise;
				},
				'requestError': function (rejection) {
					var log = loggerFactory.getLogger('requestError');
					if (rejection.config && rejection.config.url.indexOf("do/") >= 0) {
						if (!isExistSuffix(rejection.config.url)) $injector.get('modalService').closeLoading();
					}
					if (rejection.config && rejection.config.url == "do/p/signonCheck") {
						rejection.config.data = null;
					}
					log.debug(angular.toJson(rejection));
					return $q.reject(rejection);
				},
				'responseError': function (rejection) {
					var deferred = $q.defer();
					var log = loggerFactory.getLogger('responseError'), csrfToken, transToken;
					var httpErrorStatus = {
						'401': Constants.NOTIFY_UNAUTHORIZED,
						'404': Constants.NOTIFY_PAGE_NOT_FOUNT,
						'-1': Constants.NOTIFY_PAGE_NOT_FOUNT,
						'500': Constants.NOTIFY_SYSTEM_ERROR,
						'501': Constants.NOTIFY_CUSTOMER_ERROR
					};
					csrfToken = Constants.CSRF_TOKEN ? rejection.headers(Constants.CSRF_TOKEN) : undefined;
					transToken = Constants.TRANSACTION_TOKEN ? rejection.headers(Constants.TRANSACTION_TOKEN) : undefined;
					if (csrfToken) {
						storageService.setItemToSession(Constants.CSRF_TOKEN, csrfToken);
					}
					if (transToken) {
						storageService.setItemToSession(Constants.TRANSACTION_TOKEN, transToken);
					}
					if (rejection.config && rejection.config.url.indexOf("do/p/signonCheck") >= 0) {
						rejection.config.data = "******";
					}
					log.debug(angular.toJson(rejection));
					/*if (angular.isObject(rejection) && rejection.errorCode == 'DUPLUCATE_REQUEST') {
						return deferred.reject(rejection.data);
					}*/
					if (rejection.headers) {
						csrfToken = Constants.CSRF_TOKEN ? rejection.headers(Constants.CSRF_TOKEN) : undefined;
						transToken = Constants.TRANSACTION_TOKEN ? rejection.headers(Constants.TRANSACTION_TOKEN) : undefined;
						if (csrfToken) {
							storageService.setItemToSession(Constants.CSRF_TOKEN, csrfToken);
						}
						if (transToken) {
							storageService.setItemToSession(Constants.TRANSACTION_TOKEN, transToken);
						}
					}
					if (rejection.config && rejection.config.url.indexOf("do/") >= 0) {
						if (!isExistSuffix(rejection.config.url)) $injector.get('modalService').closeLoading();
					}
					if (rejection.status != 404 || rejection.config.url.indexOf("/E404") < 0) {
						$rootScope.$emit(httpErrorStatus[rejection.status], rejection.data);
					}
					if (rejection.status == 401 || rejection.status == 501) {
						deferred.reject(rejection.data);
					}
					return deferred.promise;
				}
			};
			return interceptor;
		}
	}

	httpParamSerializerProvider.$inject = [];
	function httpParamSerializerProvider() {
		this.$get = httpParamSerializer;
		httpParamSerializer.$inject = ['$injector', 'formatService', 'storageService'];
		function httpParamSerializer($injector, $filter) {
			return function (params) {
				if (!params)
					return '';
				angular.forEach(params, function (value, key) {
					if (value === null || angular.isUndefined(value))
						return;
					if (angular.isDate(value)) {
						params[key] = formatService.formatDate(value, 'longTimestamp');
					}
				});
				return $injector.get('$HttpParamSerializer')(params);
			};
		}
	}

	transformHttpRequestAndResponse.$inject = ['$http', '$filter', 'formatService', 'storageService'];
	function transformHttpRequestAndResponse($http, $filter, formatService, storageService) {
		// $http.defaults.paramSerializer = 'httpParamSerializer';
		$http.defaults.transformRequest.unshift(formatRequestDate);
		$http.defaults.transformResponse.push(parseResponseDate);

		function formatRequestDate(requestData) {
			var formatter = storageService.getFormatInfo().longTimestamp;
			function formatDate(requestData) {
				angular.forEach(requestData, function (value, key) {
					// key fieldCtrl may be cause circle error
					if (!requestData.hasOwnProperty(key) || 'ruleInfos' == key || 'rules' == key || 'fieldCtrl' == key)
						return;
					if (angular.isDate(value)) {
						requestData[key] = $filter('date')(value, formatter);//formatService.formatDate(value, 'longTimestamp');
					} else if (angular.isObject(value)) {
						formatDate(value);
					}
				});
			}
			if (!formatter) {
				return requestData;
			}
			if (angular.isObject(requestData) && !isFile(requestData) && !isBlob(requestData) && !isFormData(requestData)) {
				formatDate(requestData);
			}
			return requestData;
		}

		function parseResponseDate(responseData, headers, status) {
			var formatter, fsDatePattern;
			function parse(responseData) {
				angular.forEach(responseData, function (value, key) {
					if (!responseData.hasOwnProperty(key))
						return;
					if (angular.isString(value) && fsDatePattern.test(value)) {
						responseData[key] = formatService.parseDate(value, formatter);
					} else if (angular.isObject(value)) {
						parse(value);
					}
				});
			}
			if (!angular.isObject(responseData) || isFile(responseData) || isBlob(responseData) || isFormData(responseData)) {
				return responseData;
			}
			formatter = storageService.getFormatInfo().longTimestamp;
			if (status != 200 || !formatter) {
				return responseData;
			}
			fsDatePattern = new RegExp(formatter.replace('.', '\\.').replace(/[ydhHMmsS]/g, '\\d').replace(/Z/g, '[-+]\\d{4}'));
			parse(responseData);
			return responseData;
		}

		function isFile(obj) {
			try {
				return toString.call(obj) === '[object File]';
			} catch (e) {
				return false;
			}
		}

		function isFormData(obj) {
			try {
				return toString.call(obj) === '[object FormData]';
			} catch (e) {
				return false;
			}
		}

		function isBlob(obj) {
			try {
				return toString.call(obj) === '[object Blob]';
			} catch (e) {
				return false;
			}
		}
	}
})(angular);